名域 ts {
    /* @internal */
    导出 函数 跟踪(主机: 模块解析主机_, 消息: 诊断信息_, ...args: 任意[]): 无值;
    导出 函数 跟踪(主机: 模块解析主机_): 无值 {
        主机.跟踪!(格式化消息.应用(未定, arguments));
    }

    /* @internal */
    导出 函数 是跟踪启用(编译选项: 编译选项_, 主机: 模块解析主机_): 真假 {
        返回 !!编译选项.跟踪解析! && 主机.跟踪 !== 未定;
    }

    函数 包含KlangId(r: 解析的_ | 未定): 解析的_ | 未定 {
        返回 r ? { 路径: r.路径 } : 未定;
    }

    /** Result of trying to resolve a module. */
    接口 解析的_ {
        路径: 文字
    }

    函数 创建解析模块包括失败查找位置(解析的: 解析的_ | 未定): 解析模块包括失败的查找位置_ {
        返回 {
            模块目录路径: 解析的 && { 解析的模块目录路径: 解析的.路径! },
        };
    }

    接口 模块解析状态_ {
        主机: 模块解析主机_;
        编译选项组: 编译选项_;
        跟踪启用: 真假;
    }

    /** Reads from 'main' or '类型组'/'typings' depending on `extensions`. */
    函数 尝试读KlangJson字段(json内容: KlangJson_, 基目录: 文字, 状态: 模块解析状态_): 文字 | 未定 {
        返回 尝试读字段('名称');

        函数 尝试读字段(字段名: '名称'): 文字 | 未定 {
            如果 (!具有属性(json内容, 字段名)) {
                如果 (状态.跟踪启用) {
                    跟踪(状态.主机, 诊断信息_.package_json_does_not_have_a_0_field, 字段名);
                }
                返回;
            }

            常量 文件名 = json内容[字段名];
            如果 (!是文字(文件名)) {
                如果 (状态.跟踪启用) {
                    跟踪(状态.主机, 诊断信息_.Expected_type_of_0_field_in_package_json_to_be_string_got_1, 字段名, 类为 文件名);
                }
                返回;
            }

            常量 路径 = 正规化路径(合并路径(基目录, 文件名));
            如果 (状态.跟踪启用) {
                跟踪(状态.主机, 诊断信息_.package_json_has_0_field_1_that_references_2, 字段名, 文件名, 路径);
            }
            返回 路径;
        }
    }

    导出 接口 获取有效类型根主机_ {
        目录存在?(目录名称: 文字): 真假;
        获取当前目录?(): 文字;
    }

    /**
     * Cached module resolutions per containing directory.
     * This assumes that any module id will have the same resolution for sibling files located in the same folder.
     */
    导出 接口 模块解析缓存_ 扩展 非相对模块名称解析缓存_ {
        获取或创建缓存从目录(目录名称: 文字): 词典_<解析模块包括失败的查找位置_>;
    }

    /**
     * Stored map from non-relative module 名称 to a table: directory -> 结果 of module lookup in this directory
     * We support only non-relative module names because resolution of relative module names is usually more deterministic and thus less expensive.
     */
    导出 接口 非相对模块名称解析缓存_ {
        获取或创建缓存从模块名称(没解析的模块名称: 文字): 预处理模块名称缓存_;
    }

    导出 接口 预处理模块名称缓存_ {
        获取(目录: 文字): 解析模块包括失败的查找位置_;
        设置(目录: 文字, 结果: 解析模块包括失败的查找位置_): 无值;
    }

    导出 函数 创建模块解析缓存(当前目录: 文字, 获取规范文件名称: (s: 文字) => 文字): 模块解析缓存_ {
        返回 创建模块解析缓存包括词典(
            创建词典<词典_<解析模块包括失败的查找位置_>>(),
            创建词典<预处理模块名称缓存_>(),
            当前目录,
            获取规范文件名称
        );
    }

    /*@internal*/
    导出 函数 创建模块解析缓存包括词典( 目录到模块名称词典: 词典_<词典_<解析模块包括失败的查找位置_>>, 模块名称到目录词典: 词典_<预处理模块名称缓存_>, 当前目录: 文字, 获取规范文件名称: 获取规范文件名称_): 模块解析缓存_ {

        返回 { 获取或创建缓存从目录, 获取或创建缓存从模块名称 };

        函数 获取或创建缓存从目录(目录名称: 文字) {
            常量 路径 = 转为路径(目录名称, 当前目录, 获取规范文件名称);
            变量 每个文件夹缓存 = 目录到模块名称词典.获取(路径);
            如果 (!每个文件夹缓存) {
                每个文件夹缓存 = 创建词典<解析模块包括失败的查找位置_>();
                目录到模块名称词典.设置(路径, 每个文件夹缓存);
            }
            返回 每个文件夹缓存;
        }

        函数 获取或创建缓存从模块名称(非相对模块名称: 文字) {
            变量 每个模块名称缓存 = 模块名称到目录词典.获取(非相对模块名称);
            如果 (!每个模块名称缓存) {
                每个模块名称缓存 = 创建每个模块名称缓存();
                模块名称到目录词典.设置(非相对模块名称, 每个模块名称缓存);
            }
            返回 每个模块名称缓存;
        }

        函数 创建每个模块名称缓存(): 预处理模块名称缓存_ {
            常量 目录路径词典 = 创建词典<解析模块包括失败的查找位置_>();

            返回 { 获取, 设置 };

            函数 获取(目录: 文字): 解析模块包括失败的查找位置_ {
                返回 目录路径词典.获取(转为路径(目录, 当前目录, 获取规范文件名称))!;
            }

            /**
             * At 首个 this function add entry directory -> module resolution 结果 to the table.
             * Then it computes the set of 父节点 folders for 'directory' that should have the same module resolution 结果
             * and for every 父节点 folder in set it adds entry: 父节点 -> module resolution. .
             * Lets say we 首个 directory 名称: /a/b/c/d/e and resolution 结果 is: /a/b/bar.ts.
             * Set of 父节点 folders that should have the same 结果 will be:
             * [
             *     /a/b/c/d, /a/b/c, /a/b
             * ]
             * this means that request for module resolution from 文件 in any of these folder will be immediately found in cache.
             */
            函数 设置(目录: 文字, 结果: 解析模块包括失败的查找位置_): 无值 {
                常量 路径 = 转为路径(目录, 当前目录, 获取规范文件名称);
                // if entry is already in cache do nothing
                如果 (目录路径词典.具有(路径)) {
                    返回;
                }
                目录路径词典.设置(路径, 结果);

                常量 解析文件的名称 = 结果.模块目录路径 && 结果.模块目录路径.解析的模块目录路径;
                // find common 前缀 between directory and resolved 文件 名称
                // this common 前缀 should be the shorted 路径 that has the same resolution
                // directory: /a/b/c/d/e
                // resolvedFileName: /a/b/foo.d.ts
                常量 公共前缀 = 获取公共前缀(路径, 解析文件的名称!);
                变量 当前 = 路径;
                判断 (为真) {
                    常量 父节点 = 获取目录路径(当前);
                    如果 (父节点 === 当前 || 目录路径词典.具有(父节点)) {
                        跳出;
                    }
                    目录路径词典.设置(父节点, 结果);
                    当前 = 父节点;

                    如果 (当前 === 公共前缀) {
                        跳出;
                    }
                }
            }

            函数 获取公共前缀(目录: 路径_, 解析的: 文字) {
                如果 (解析的 === 未定) {
                    返回 未定;
                }
                常量 解析目录 = 转为路径(获取目录路径(解析的), 当前目录, 获取规范文件名称);

                // find 首个 position where directory and resolution differs
                变量 i = 0;
                判断 (i < 数学_.min(目录.长度, 解析目录.长度) && 目录.字符代码在(i) === 解析目录.字符代码在(i)) {
                    i++;
                }

                // find 最后的 directory separator before position i
                常量 分割 = 目录.最后索引位于(目录分隔符, i);
                如果 (分割 < 0) {
                    返回 未定;
                }

                返回 目录.子文本(0, 分割);
            }
        }
    }

    导出 函数 解析模块名称(模块名称: 文字, 包含文件: 文字, 编译选项: 编译选项_, 主机: 模块解析主机_, 外部依赖: 词典_<文字>, 缓存?: 模块解析缓存_): 解析模块包括失败的查找位置_ {
        常量 跟踪启用 = 是跟踪启用(编译选项, 主机);
        如果 (跟踪启用) {
            跟踪(主机, 诊断信息_.Resolving_module_0_from_1, 模块名称, 包含文件);
        }
        常量 包含目录 = 获取目录路径(包含文件);
        常量 每个文件夹缓存 = 缓存 && 缓存.获取或创建缓存从目录(包含目录);
        变量 结果 = 每个文件夹缓存 && 每个文件夹缓存.获取(模块名称);

        如果 (结果) {
            如果 (跟踪启用) {
                跟踪(主机, 诊断信息_.Resolution_for_module_0_was_found_in_cache_from_location_1, 模块名称, 包含目录);
            }
        }
        否则 {

            变量 本地模块 = 正规化路径(合并路径(主机.当前源码目录, 模块名称))

            如果 (目录可能存在(本地模块, 主机)) {
                变量 临时 = {
                    解析的模块目录路径: 本地模块,
                    是外部支持库导入: 模块导入位置种类_.无_
                }
                结果 = {
                    模块目录路径: 临时
                }
            }
            
            常量 依赖详细版本 = 外部依赖!.获取(模块名称)
            如果 (!结果) {
                如果 (依赖详细版本) {                    
                    变量 临时 = {
                        解析的模块目录路径: 正规化路径(合并路径(主机.当前模块目录, 依赖详细版本)),
                        是外部支持库导入: 模块导入位置种类_.外部支持库_
                    }
                    结果 = {
                        模块目录路径: 临时
                    }
                }
            }

            如果 (!结果) {
                如果 (依赖详细版本) {
                    变量 临时 = {
                        解析的模块目录路径: 正规化路径(合并路径(主机.全局模块目录, 依赖详细版本)),
                        是外部支持库导入: 模块导入位置种类_.全局支持库_
                    }
                    结果 = {
                        模块目录路径: 临时
                    }
                }
            }

            如果 (每个文件夹缓存 && 结果) {
                每个文件夹缓存.设置(模块名称, 结果);
                常量 每个模块名缓存 = 缓存!.获取或创建缓存从模块名称(模块名称);
                如果 (每个模块名缓存) {
                    每个模块名缓存.设置(包含目录, 结果);
                }
            }
        }

        如果 (跟踪启用) {
            如果 (结果!.模块目录路径) {
                跟踪(主机, 诊断信息_.Module_name_0_was_successfully_resolved_to_1, 模块名称, 结果!.模块目录路径.解析的模块目录路径);
            }
            否则 {
                跟踪(主机, 诊断信息_.Module_name_0_was_not_resolved, 模块名称);
            }
        }

        返回 结果 || {};
    }

    函数 按相对名加载模块(候选: 文字, 仅记录失败的: 真假, 状态: 模块解析状态_, 考虑包内容: 真假): 解析的_ | 未定 {
        如果 (状态.跟踪启用) {
            跟踪(状态.主机, 诊断信息_.Loading_module_as_file_Slash_folder_candidate_module_location_0_target_file_type_1, 候选);
        }
        如果 (!路径以分隔符结尾(候选)) {
            如果 (!仅记录失败的) {
                常量 候选的父级 = 获取目录路径(候选);
                如果 (!目录可能存在(候选的父级, 状态.主机)) {
                    如果 (状态.跟踪启用) {
                        跟踪(状态.主机, 诊断信息_.Directory_0_does_not_exist_skipping_all_lookups_in_it, 候选的父级);
                    }
                    仅记录失败的 = 为真;
                }
            }
            常量 解析从文件 = 加载模块从文件(候选,  仅记录失败的, 状态);
            如果 (解析从文件) {
                返回 包含KlangId(解析从文件);
            }
        }
        如果 (!仅记录失败的) {
            常量 候选存在 = 目录可能存在(候选, 状态.主机);
            如果 (!候选存在) {
                如果 (状态.跟踪启用) {
                    跟踪(状态.主机, 诊断信息_.Directory_0_does_not_exist_skipping_all_lookups_in_it, 候选);
                }
                仅记录失败的 = 为真;
            }
        }
        返回 加载模块从目录(候选, 仅记录失败的, 状态, 考虑包内容);
    }

    /* @internal */
    导出 函数 目录可能存在(目录名: 文字, 主机: { 目录存在?: (目录名: 文字) => 真假 }): 真假 {
        // if host does not support 'directoryExists' assume that directory will exist
        返回 !主机.目录存在 || 主机.目录存在(目录名);
    }

    /**
     * @param {boolean} 仅记录失败 - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary
     * in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations.
     */
    函数 加载模块从文件(候选: 文字,  仅记录失败: 真假, 状态: 模块解析状态_): 解析的_ | 未定 {
        如果 (!仅记录失败) {
            // check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
            常量 目录 = 获取目录路径(候选);
            如果 (目录) {
                仅记录失败 = !目录可能存在(目录, 状态.主机);
            }
        }
        常量 路径 = 尝试从文件(候选, 仅记录失败, 状态);
        返回 路径 ? { 路径 } : 未定;
    }

    /** Return the 文件 if it exists. */
    函数 尝试从文件(目录: 文字, 仅记录失败的: 真假, 状态: 模块解析状态_): 文字 | 未定 {
        如果 (!仅记录失败的) {
            如果 (状态.主机.目录存在!(目录)) {
                如果 (状态.跟踪启用) {
                    跟踪(状态.主机, 诊断信息_.File_0_exist_use_it_as_a_name_resolution_result, 目录);
                }
                返回 目录;
            }
            否则 {
                如果 (状态.跟踪启用) {
                    跟踪(状态.主机, 诊断信息_.File_0_does_not_exist, 目录);
                }
            }
        }
        返回 未定;
    }

    函数 加载模块从目录(候选: 文字, 仅记录失败的: 真假, 状态: 模块解析状态_, 考虑KlangJson = 为真) {
        常量 { 包内容 } = 考虑KlangJson
            ? 获取Klang包信息(候选,  仅记录失败的, 状态)
            : { 包内容: 未定 };
        返回 包含KlangId(加载模块从目录平台(候选,  仅记录失败的, 状态, 包内容));
    }

    函数 加载模块从目录平台(候选: 文字,  仅记录失败的: 真假, 状态: 模块解析状态_, klang包内容: KlangJson_ | 未定): 解析的_ | 未定 {
        常量 从包内容 = klang包内容 && 加载模块从Klang包(klang包内容, 候选, 状态);
        如果 (从包内容) {
            返回 从包内容;
        }
        常量 目录存在 = !仅记录失败的 && 目录可能存在(候选, 状态.主机);
        返回 加载模块从文件(候选,  !目录存在, 状态);
    }

    函数 获取Klang包信息( 模块目录: 文字,  仅记录失败的: 真假, 状态: 模块解析状态_, ): { 找到: 真假, 包内容: KlangJson_ | 未定, 包Id: 包Id_ | 未定 } {
        常量 { 主机, 跟踪启用 } = 状态;
        常量 目录存在 = !仅记录失败的 && 目录可能存在(模块目录, 主机);
        常量 klang包路径 = 路径转为KlangJson(模块目录);

        如果 (目录存在 && 主机.文件存在(klang包路径)) {
            常量 klang包内容 = 读Json(klang包路径, 主机);

            常量 klangId: 包Id_ = 类为 klang包内容.名称 === '文字' && 类为 klang包内容.版本 === '文字'
                ? { 名称: klang包内容.名称, 版本: klang包内容.版本 }
                : 未定!;
            如果 (跟踪启用) {
                如果 (klangId) {
                    跟踪(主机, 诊断信息_.Found_package_json_at_0_Package_ID_is_1, klang包路径, klangId.名称);
                }
                否则 {
                    跟踪(主机, 诊断信息_.Found_package_json_at_0, klang包路径);
                }
            }
            返回 { 找到: 为真, 包内容: klang包内容, 包Id: klangId };
        }
        否则 {
            如果 (目录存在 && 跟踪启用) {
                跟踪(主机, 诊断信息_.File_0_does_not_exist, klang包路径);
            }
            返回 { 找到: 为假, 包内容: 未定, 包Id: 未定 };
        }
    }

    函数 加载模块从Klang包(json内容: KlangJson_, 候选: 文字,  状态: 模块解析状态_): 解析的_ | 未定 {
        常量 文件 = 尝试读KlangJson字段(json内容, 候选, 状态);
        如果 (!文件) {
            返回 未定;
        }

        常量 仅记录失败 = !目录可能存在(获取目录路径(文件), 状态.主机);
        常量 路径 = 尝试从文件(文件, 仅记录失败, 状态);
        如果 (路径) {
            返回 { 路径 };
        }
        如果 (状态.跟踪启用) {
            跟踪(状态.主机, 诊断信息_.File_0_has_an_unsupported_extension_so_skipping_it, 路径);
        }
        // Don't do package.json lookup recursively, because 节点_.js' package lookup doesn't.
        常量 结果 = 按相对名加载模块(文件,  仅记录失败, 状态, /*considerPackageJson*/ 为假);
        如果 (结果) {
            返回 { 路径: 结果.路径 };
        }
    }

    函数 路径转为KlangJson(目录: 文字): 文字 {
        返回 合并路径(目录, 'klang.mod.json');
    }

    函数 加载模块从模块文件夹(模块名称: 文字, 模块文件夹: 文字, 模块文件夹存在: 真假, 状态: 模块解析状态_): 解析的_ | 未定 {
        常量 候选 = 正规化路径(合并路径(模块文件夹, 模块名称));
        // First look for a nested package.json, as in `node_modules/foo/bar/package.json`.
        变量 klang包内容: KlangJson_ | 未定;
        常量 klang包信息 = 获取Klang包信息(候选, /*onlyRecordFailures*/ !模块文件夹存在, 状态);
        如果 (klang包信息.找到) {
            ({ 包内容: klang包内容 } = klang包信息);
        }
        常量 路径和扩展名 = 加载模块从文件(候选, !模块文件夹存在, 状态) || 加载模块从目录平台(候选,  !模块文件夹存在, 状态, klang包内容);
        返回 包含KlangId(路径和扩展名);
    }

    /** Load a module from a single node_modules directory, but not from any ancestors' node_modules directories. */
    函数 加载模块从模块一级(模块名称: 文字, 目录: 文字, 状态: 模块解析状态_, ): 解析的_ | 未定 {
        常量 模块文件夹 = 合并路径(目录, '模块');
        常量 模块文件夹存在 = 目录可能存在(模块文件夹, 状态.主机);
        如果 (!模块文件夹存在 && 状态.跟踪启用) {
            跟踪(状态.主机, 诊断信息_.Directory_0_does_not_exist_skipping_all_lookups_in_it, 模块文件夹);
        }

        返回 加载模块从模块文件夹(模块名称, 模块文件夹, 模块文件夹存在, 状态);
    }


    /**
     * LSHost may load a module from a global cache of typings.
     * This is the minumum code needed to expose that functionality; the rest is in LSHost.
     */
    /* @internal */
    导出 函数 加载模块从全局缓存(模块名称: 文字, 项目名称: 文字, 编译选项: 编译选项_, 主机: 模块解析主机_, 全局缓存: 文字): 解析模块包括失败的查找位置_ {
        常量 跟踪启用 = 是跟踪启用(编译选项, 主机);
        如果 (跟踪启用) {
            跟踪(主机, 诊断信息_.Auto_discovery_for_typings_is_enabled_in_project_0_Running_extra_resolution_pass_for_module_1_using_cache_location_2, 项目名称, 模块名称, 全局缓存);
        }
        常量 状态: 模块解析状态_ = { 编译选项组: 编译选项, 主机: 主机, 跟踪启用: 跟踪启用 };
        常量 解析的 = 加载模块从模块一级(模块名称, 全局缓存, 状态);
        返回 创建解析模块包括失败查找位置(解析的);
    }

}
